/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package se.kth.lwr.jeat.explorer;

import se.kth.lwr.jeat.explorer.JeatExplorer;
import java.awt.HeadlessException;
import java.awt.event.ItemEvent;
import java.awt.event.ItemListener;
import java.util.*;
import javax.measure.unit.Unit;
import javax.swing.DefaultComboBoxModel;
import javax.swing.ImageIcon;
import javax.swing.JTable;
import javax.swing.event.TreeSelectionEvent;
import javax.swing.event.TreeSelectionListener;
import javax.swing.table.DefaultTableModel;
import javax.swing.tree.*;
import org.jscience.physics.amount.Amount;
import se.kth.lwr.jeat.core.*;
import se.kth.lwr.jeat.core.Report.ReportRecord;
import se.kth.lwr.jeat.utils.UmberellaActivity;

/**
 * The Jframe that delivers the Explorer GUI.
 *
 * @author davidev
 */
public class JeatGUI extends javax.swing.JFrame {
    /*
     * A reference to all Models (Activities) that should be explored
     */

    private List<Activity> activities;
    /*
     * A reference to all Impact Models that should be applied
     */
    private Set<ImpactModel> ipModels;
    /*
     * A custom class used for managing A Jtable that delivers information of
     * selected items in the different treeviews of the GUI
     */
    ReportTableManager repTableManager;
    /*
     * A reference to the explorer that initialized the GUI
     */
    JeatExplorer explorer;

    /**
     * Creates new form JeatGUI
     */
    public JeatGUI(JeatExplorer explorer) {
        initComponents();
        repTableManager = new ReportTableManager();
        this.explorer = explorer;
        setIconImage(new ImageIcon(getClass().getResource("../icon/jeat16.png")).getImage());
    }
    
    
    /**Setup all necessary data in the JFrame
     * 
     * @param activities Models to visualize
     * @param ipModels ImpactModels to apply
     */
    protected void setup(List<Activity> activities, Set<ImpactModel> ipModels) {
        //store data for the GUI
        this.activities = activities;
        this.ipModels = ipModels;

        //Import data for the Treeview of activities and selection handler
        setupActivityTree();
        //Setup data for the Category ComboBox

        setupCategoryComboBox();
        //Import data for the Treeview of dataSources
        setupDataSourceTree();


    }

    /**
     * prepare combobox to be able to filter on different categories
     */
    private void setupCategoryComboBox() {
        //prepare list content, using objects to be able to add a String
        //for representation of "no selection"
        List<Object> objects = new LinkedList<>();
        //add content
        objects.addAll(ActivityCategory.categories());
        //adds "no selection"
        objects.add(0, "No selection");
        //create combobox model
        DefaultComboBoxModel<Object> model = new DefaultComboBoxModel<>(objects.toArray());
        //add model to combobox
        navCategoryComboBox.setModel(model);
        //Add a listned to enforce new calculation of report when selection is changed
        navCategoryComboBox.addItemListener(new ItemListener() {
            @Override
            public void itemStateChanged(ItemEvent e) {
                //enforce new calculation
                updateActivityReport();
            }
        });
    }

    /**
     * Prepares and adds data to the Treeview of activities
     */
    private void setupActivityTree() {

        //create an emty place holder for the models that should be visualized
        Activity root = new UmberellaActivity("ROOT", "Umberella for all models that should be visualized");
        
        //add selected models to the root
        for (Activity activity : activities) {
            root.addChild(activity);
        }

        //create a TreeModel based on the root node and add it to the treeView
        navActivityTree.setModel(new DefaultTreeModel(root));

        //add a Selectionlistener for updating the report table
        navActivityTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                //enforce new calculation
                updateActivityReport();
            }
        });

    }
    
    /**
     * Updates the report table with data from selected activity
     */
    private void updateActivityReport() {

        //get selected treenode
        Object obj = navActivityTree.getSelectionPath().getLastPathComponent();
        //return if no selection
        if (obj == null) {
            return;
        }
        //clear table
        repTableManager.clearTable();
        //get selected category (will be used as filter)
        ActivityCategory category = getSelectedActivity();
        //check that we really have an Activity
        if (obj instanceof Activity) {
            //cast
            Activity activity = (Activity) obj;
            //view the impacts..
            //print title
            repTableManager.printTableTitle("Caused impact(s)");
            //print impacts for all seleced ImpactModels
            for (ImpactModel model : ipModels) {
                repTableManager.printImpact(model, activity.getInventory(category));
            }
            //view the Raw inventory...
            //title
            repTableManager.printTableTitle("Raw inventory");
            //vaues
            repTableManager.printReport(activity.getInventory(category));
            
            // view used data sources...
            //get the sources
            Set<DataSource> sources = activity.getSources();
            //check for data
            if (!sources.isEmpty()) {
                //print title
                repTableManager.printTableTitle("Used datasources");
                //print values for all data sources
                for (DataSource dataSource : sources) {
                    repTableManager.printReport(dataSource);
                }
            }
            
            //view joined activityCategories..
            //get them
            Set<ActivityCategory> categories = activity.joinedCategories();
            //check for data
            if (!categories.isEmpty()) {
                //print title
                repTableManager.printTableTitle("Joined categories");
                //print all categories
                for (ActivityCategory activityCategory : categories) {
                    repTableManager.printTableRow(activityCategory, null, null, null);

                }
            }
        }


    }

    /**
     *
     * @return the selected ActivityCategory for filtering of report data or
     * null if no category is selected
     */
    private ActivityCategory getSelectedActivity() {
        Object select = navCategoryComboBox.getModel().getSelectedItem();
        if (select instanceof ActivityCategory) {
            return (ActivityCategory) select;
        } else {
            return null;
        }

    }
    
    /**
     * Setup the tree showing all available datasources and their usage
     */

    private void setupDataSourceTree() {
        //create an emty place holder for the models that should be visualized
        DefaultMutableTreeNode root = new DefaultMutableTreeNode("Umberella for all datasources");
        //add all available datasources to the root
        for (DataSource source : DataSource.getDataSources()) {
            root.add(new DefaultMutableTreeNode(source));
        }
        //create a TreeModel based on the root node and add it to the treeView
        navSourceDataTree.setModel(new DefaultTreeModel(root));
        //listen to selection changes and update report table accoringly
        navSourceDataTree.getSelectionModel().addTreeSelectionListener(new TreeSelectionListener() {

            @Override
            public void valueChanged(TreeSelectionEvent e) {
                updateSourceReport();
            }
        });

    }
    
    /**
     * Update the DataSource data in the report table
     */
    private void updateSourceReport() {
        
        // get selection
        DefaultMutableTreeNode node = (DefaultMutableTreeNode) navSourceDataTree.getSelectionPath().getLastPathComponent();
        //check for null
        if(node==null){
            return;
        }
        //clear table
        repTableManager.clearTable();
        // get the object
        Object obj = node.getUserObject();
        //ensure we have a datasource
        if (obj instanceof DataSource) {
            //Cast
            DataSource<?> source = (DataSource<?>) obj;
            //print source info
            repTableManager.printReport(source);
            //show users
            repTableManager.printTableTitle("Users:");
            Set<Activity> users = source.getUsers();
            for (Activity activity : users) {
                repTableManager.printTableRow(activity.getName(), null, null, activity.getDescription());
            }
        }

    }

    /**
     * This method is called from within the constructor to initialize the form.
     * WARNING: Do NOT modify this code. The content of this method is always
     * regenerated by the Form Editor.
     */
    @SuppressWarnings("unchecked")
    // <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
    private void initComponents() {

        reportButtonGroup = new javax.swing.ButtonGroup();
        jSplitPane = new javax.swing.JSplitPane();
        navPanel = new javax.swing.JPanel();
        navTabbedPane = new javax.swing.JTabbedPane();
        navActivityPanel = new javax.swing.JPanel();
        navActivityScrollPane = new javax.swing.JScrollPane();
        navActivityTree = new javax.swing.JTree();
        navSourceDataPanel = new javax.swing.JPanel();
        navSourceDataScrollPane = new javax.swing.JScrollPane();
        navSourceDataTree = new javax.swing.JTree();
        filterPanel = new javax.swing.JPanel();
        navCategoryComboBox = new javax.swing.JComboBox<Object>();
        jLabel2 = new javax.swing.JLabel();
        repPanel = new javax.swing.JPanel();
        RepScrollPane = new javax.swing.JScrollPane();
        repTable = new javax.swing.JTable();
        main_jMenuBar = new javax.swing.JMenuBar();
        file_jMenu = new javax.swing.JMenu();
        closeItem = new javax.swing.JMenuItem();

        setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
        setTitle("Jeat explorer");

        jSplitPane.setDividerLocation(200);

        navPanel.setMinimumSize(new java.awt.Dimension(100, 62));
        navPanel.setLayout(new java.awt.BorderLayout());

        navTabbedPane.setMinimumSize(new java.awt.Dimension(100, 40));
        navTabbedPane.setName("");

        navActivityPanel.setMinimumSize(new java.awt.Dimension(100, 0));
        navActivityPanel.setPreferredSize(new java.awt.Dimension(273, 574));
        navActivityPanel.setLayout(new java.awt.BorderLayout());

        navActivityScrollPane.setBorder(null);

        navActivityTree.setMaximumSize(new java.awt.Dimension(0, 0));
        navActivityTree.setMinimumSize(new java.awt.Dimension(100, 200));
        navActivityTree.setRootVisible(false);
        navActivityScrollPane.setViewportView(navActivityTree);

        navActivityPanel.add(navActivityScrollPane, java.awt.BorderLayout.CENTER);

        navTabbedPane.addTab("Activities", navActivityPanel);

        navSourceDataPanel.setMinimumSize(new java.awt.Dimension(100, 0));
        navSourceDataPanel.setPreferredSize(new java.awt.Dimension(273, 574));
        navSourceDataPanel.setLayout(new java.awt.BorderLayout());

        navSourceDataTree.setMaximumSize(new java.awt.Dimension(0, 0));
        navSourceDataTree.setRootVisible(false);
        navSourceDataScrollPane.setViewportView(navSourceDataTree);

        navSourceDataPanel.add(navSourceDataScrollPane, java.awt.BorderLayout.CENTER);

        navTabbedPane.addTab("Source data", navSourceDataPanel);

        navPanel.add(navTabbedPane, java.awt.BorderLayout.CENTER);
        navTabbedPane.getAccessibleContext().setAccessibleName("Hej");

        navCategoryComboBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "Item 1", "Item 2", "Item 3", "Item 4" }));

        jLabel2.setText("Select catagory for report filtering");

        javax.swing.GroupLayout filterPanelLayout = new javax.swing.GroupLayout(filterPanel);
        filterPanel.setLayout(filterPanelLayout);
        filterPanelLayout.setHorizontalGroup(
            filterPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(filterPanelLayout.createSequentialGroup()
                .addContainerGap()
                .addGroup(filterPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
                    .addGroup(filterPanelLayout.createSequentialGroup()
                        .addComponent(jLabel2)
                        .addGap(0, 15, Short.MAX_VALUE))
                    .addComponent(navCategoryComboBox, 0, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
                .addContainerGap())
        );
        filterPanelLayout.setVerticalGroup(
            filterPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
            .addGroup(filterPanelLayout.createSequentialGroup()
                .addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
                .addComponent(jLabel2)
                .addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
                .addComponent(navCategoryComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
                .addGap(16, 16, 16))
        );

        navPanel.add(filterPanel, java.awt.BorderLayout.PAGE_END);

        jSplitPane.setLeftComponent(navPanel);

        repPanel.setLayout(new java.awt.BorderLayout());

        repTable.setModel(new javax.swing.table.DefaultTableModel(
            new Object [][] {
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null},
                {null, null, null, null}
            },
            new String [] {
                "Key", "Value", "Unit", "Comment"
            }
        ));
        RepScrollPane.setViewportView(repTable);

        repPanel.add(RepScrollPane, java.awt.BorderLayout.CENTER);

        jSplitPane.setRightComponent(repPanel);

        getContentPane().add(jSplitPane, java.awt.BorderLayout.CENTER);

        file_jMenu.setText("File");

        closeItem.setText("Close");
        closeItem.addActionListener(new java.awt.event.ActionListener() {
            public void actionPerformed(java.awt.event.ActionEvent evt) {
                closeItemActionPerformed(evt);
            }
        });
        file_jMenu.add(closeItem);

        main_jMenuBar.add(file_jMenu);

        setJMenuBar(main_jMenuBar);

        pack();
    }// </editor-fold>//GEN-END:initComponents

    /*
     * Closes the program
     */
    private void closeItemActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_closeItemActionPerformed
        // TODO add your handling code here:
        System.exit(0);

    }//GEN-LAST:event_closeItemActionPerformed
    // Variables declaration - do not modify//GEN-BEGIN:variables
    private javax.swing.JScrollPane RepScrollPane;
    private javax.swing.JMenuItem closeItem;
    private javax.swing.JMenu file_jMenu;
    private javax.swing.JPanel filterPanel;
    private javax.swing.JLabel jLabel2;
    private javax.swing.JSplitPane jSplitPane;
    private javax.swing.JMenuBar main_jMenuBar;
    private javax.swing.JPanel navActivityPanel;
    private javax.swing.JScrollPane navActivityScrollPane;
    private javax.swing.JTree navActivityTree;
    private javax.swing.JComboBox<Object> navCategoryComboBox;
    private javax.swing.JPanel navPanel;
    private javax.swing.JPanel navSourceDataPanel;
    private javax.swing.JScrollPane navSourceDataScrollPane;
    private javax.swing.JTree navSourceDataTree;
    private javax.swing.JTabbedPane navTabbedPane;
    private javax.swing.JPanel repPanel;
    private javax.swing.JTable repTable;
    private javax.swing.ButtonGroup reportButtonGroup;
    // End of variables declaration//GEN-END:variables

    /**
     * A helper class that manage the report table
     */
    
    class ReportTableManager {
        //underlying table
        JTable table = repTable;
        // the present tablemodel
        DefaultTableModel repTableModel = (DefaultTableModel) table.getModel();
        
        //clears the hole table
        public void clearTable() {
            repTableModel.setNumRows(0);
        }

        /**
         * Fills the reportTable with data in next available table row
         *
         * @param key value of key column
         * @param value value of value column
         * @param unit value of unit column
         * @param description value of description column
         */
        private void printTableRow(Object key, Object value, Object unit, Object description) {
            Object[] row = new Object[4];
            row[0] = key;
            row[1] = value;
            row[2] = unit;
            row[3] = description;
            repTableModel.addRow(row);


        }
        
        /**Print out a hole reportable
         * 
         * @param reportable the reportable to print
         */

        private void printReport(Reportable reportable) {
            Report report = reportable.getReport();
            for (ReportRecord record : report.getReportRecords()) {
                printTableRow(record.getKey(), record.getValue(), record.getUnit(), record.getComment());
            }

        }
        
        /**Prints an Impact
         * 
         * @param model ImpactModel
         * @param inventoriable Inventoriable to apply to the model
         */

        private void printImpact(ImpactModel model, Inventoriable inventoriable) {
            printReport(model.getReportableImpact(inventoriable));
        }
        
        /**
         * Prints an Impact
         * 
         * @param model ImpactModel
         * @param inventory Inventory to apply to the model
         */

        private void printImpact(ImpactModel model, Inventory inventory) {
            printImpact(model, inventory.asInventoriable());
        }

        /**
         * Adds a title row to the reportTable with a leading empty row
         *
         * @param title
         */
        private void printTableTitle(String title) {
            printTableRow(null, null, null, null);
            printTableRow(title, null, null, null);

        }
    }
}
